package main

import (
	"context"
	"strconv"
	"time"

	"github.com/go-redis/redis/v8"
)

type RedisLeaderboard struct {
	client *redis.Client
	ctx    context.Context
}

func NewRedisLeaderboard(addr string) *RedisLeaderboard {
	client := redis.NewClient(&redis.Options{
		Addr:     addr,
		Password: "", // no password set
		DB:       0,  // use default DB
	})

	return &RedisLeaderboard{
		client: client,
		ctx:    context.Background(),
	}
}

// 更新玩家分数
func (r *RedisLeaderboard) UpdateScore(playerID string, score int) {
	// 使用当前时间戳作为分数相同时的排序依据
	timestamp := float64(time.Now().UnixNano())
	// Redis有序集合的score由玩家分数和时间戳组成
	compositeScore := float64(score) + (1 - timestamp/1e19)

	r.client.ZAdd(r.ctx, "leaderboard", &redis.Z{
		Score:  compositeScore,
		Member: playerID,
	})
}

// 获取玩家排名
func (r *RedisLeaderboard) GetPlayerRank(playerID string) (RankInfo, bool) {
	rank := r.client.ZRevRank(r.ctx, "leaderboard", playerID).Val()
	if rank == -1 {
		return RankInfo{}, false
	}

	score := r.client.ZScore(r.ctx, "leaderboard", playerID).Val()
	// 提取实际分数(去掉时间戳部分)
	actualScore := int(score)

	return RankInfo{
		PlayerID: playerID,
		Rank:     int(rank) + 1,
		Score:    actualScore,
	}, true
}

// 获取前N名
func (r *RedisLeaderboard) GetTopN(n int) []RankInfo {
	result := make([]RankInfo, 0, n)

	players := r.client.ZRevRangeWithScores(r.ctx, "leaderboard", 0, int64(n-1)).Val()
	for i, z := range players {
		playerID := z.Member.(string)
		score := int(z.Score) // 实际分数已包含在score中

		result = append(result, RankInfo{
			PlayerID: playerID,
			Rank:     i + 1,
			Score:    score,
		})
	}

	return result
}

// 获取玩家周边排名
func (r *RedisLeaderboard) GetPlayerRankRange(playerID string, rangeSize int) []RankInfo {
	rank := r.client.ZRevRank(r.ctx, "leaderboard", playerID).Val()
	if rank == -1 {
		return nil
	}

	start := max(0, int(rank)-rangeSize)
	end := int(rank) + rangeSize

	players := r.client.ZRevRangeWithScores(r.ctx, "leaderboard", int64(start), int64(end)).Val()
	result := make([]RankInfo, len(players))

	for i, z := range players {
		playerID := z.Member.(string)
		score := int(z.Score)

		result[i] = RankInfo{
			PlayerID: playerID,
			Rank:     start + i + 1,
			Score:    score,
		}
	}

	return result
}

// 实现密集排名版本
type RedisDenseLeaderboard struct {
	RedisLeaderboard
}

func NewRedisDenseLeaderboard(addr string) *RedisDenseLeaderboard {
	return &RedisDenseLeaderboard{
		RedisLeaderboard: *NewRedisLeaderboard(addr),
	}
}

// 重写GetPlayerRank实现密集排名
func (r *RedisDenseLeaderboard) GetPlayerRank(playerID string) (RankInfo, bool) {
	score := r.client.ZScore(r.ctx, "leaderboard", playerID).Val()
	if score == 0 {
		return RankInfo{}, false
	}

	// 获取所有大于等于当前分数的玩家数量
	rank := r.client.ZCount(r.ctx, "leaderboard",
		strconv.Itoa(int(score)), "+inf").Val()

	return RankInfo{
		PlayerID: playerID,
		Rank:     int(rank),
		Score:    int(score),
	}, true
}

// 重写GetTopN实现密集排名
func (r *RedisDenseLeaderboard) GetTopN(n int) []RankInfo {
	players := r.client.ZRevRangeWithScores(r.ctx, "leaderboard", 0, int64(n-1)).Val()
	result := make([]RankInfo, len(players))

	prevScore := -1.0
	currentRank := 1

	for i, z := range players {
		playerID := z.Member.(string)
		score := z.Score

		if score != prevScore {
			currentRank = i + 1
			prevScore = score
		}

		result[i] = RankInfo{
			PlayerID: playerID,
			Rank:     currentRank,
			Score:    int(score),
		}
	}

	return result
}
